package com.tiancheng.trade.payment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.paypal.http.HttpResponse;
import com.paypal.orders.Capture;
import com.paypal.orders.LinkDescription;
import com.paypal.orders.Order;
import com.paypal.orders.PurchaseUnit;
import com.paypal.payments.Refund;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.enums.PaySceneEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.PaymentWayEnum;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.payment.bo.OrgPaymentChannelDetailBO;
import com.tiancheng.trade.commom.core.data.notify.PayResultMsgBO;
import com.tiancheng.trade.payment.channel.bo.PayResultBO;
import com.tiancheng.trade.payment.channel.bo.RefundOrderBO;
import com.tiancheng.trade.payment.channel.bo.RefundResultBO;
import com.tiancheng.trade.payment.channel.paypal.PaypalPayChannel;
import com.tiancheng.trade.payment.enums.*;
import com.tiancheng.trade.payment.mapper.IntPaymentOrderMapper;
import com.tiancheng.trade.payment.mapper.IntRefundFlowMapper;
import com.tiancheng.trade.payment.model.IntPaymentOrder;
import com.tiancheng.trade.payment.model.IntRefundFlow;
import com.tiancheng.trade.payment.model.IntRefundFlowDetail;
import com.tiancheng.trade.payment.service.IntFlowDetailService;
import com.tiancheng.trade.payment.service.IntRefundFlowDetailService;
import com.tiancheng.trade.payment.service.OrgPaymentChannelService;
import com.tiancheng.trade.payment.service.PaypalOrderService;
import com.tiancheng.trade.payment.util.*;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import com.tiancheng.trade.payment.util.lock.Lock;
import com.tiancheng.trade.payment.vo.api.CloseOrderVO;
import com.tiancheng.trade.payment.vo.api.PaypalCreateReqVO;
import com.tiancheng.trade.payment.vo.api.PaypalExecuteReqVO;
import com.tiancheng.trade.payment.vo.api.PaypalRefundReqVO;
import com.tiancheng.trade.payment.vo.api.res.PaypalCreateResVO;
import com.tiancheng.trade.payment.vo.api.res.PaypalExecuteResVO;
import com.tiancheng.trade.payment.vo.api.res.PaypalQueryResVO;
import com.tiancheng.trade.payment.vo.api.res.PaypalRefundResVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PaypalOrderServiceImpl implements PaypalOrderService {


    @Resource
    private IntPaymentOrderMapper intPaymentOrderMapper;

    @Resource
    private IntRefundFlowMapper intRefundFlowMapper;


    @Resource
    private IntFlowDetailService intFlowDetailService;

    @Resource
    private IntRefundFlowDetailService intRefundFlowDetailService;


    private static final String CURRENCY = "USD";

    @Resource
    private PaypalPayChannel paypalPayChannel;
    @Resource
    private JacksonJsonUtil jacksonJsonUtil;
    @Resource
    private OrgPaymentChannelService orgPaymentChannelService;
    /**
     * 创建支付
     *
     * @param requestVO
     * @return
     * @throws
     */
    @Override
    public PaypalCreateResVO createPayment(PaypalCreateReqVO requestVO) {

        // 检查支付渠道、方式是否匹配
        BusinessErrorInfoEnum.CHANNEL_NO_MATCH.assertNotNull(EnumUtil.getEnumByCode(PaymentChannelEnum.class, requestVO.getPayChannel()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_WAY.assertNotNull(EnumUtil.getEnumByCode(PaymentWayEnum.class, requestVO.getPayWay()));
        BusinessErrorInfoEnum.CHANNEL_NO_PAY_SCENE.assertNotNull(EnumUtil.getEnumByCodeIgnoreCase(PaySceneEnum.class, requestVO.getPayScene()));
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_CREATE_PAY, requestVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("payment_id", requestVO.getPaymentId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(intPaymentOrderMapper.selectCount(conditions) == 0);
            final OrgPaymentChannelDetailBO paymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(requestVO.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getPay(requestVO.getUid(), requestVO.getPayChannel(), null,paymentChannelDetailBO.getChannelOrderIdPre());

            IntPaymentOrder intPaymentOrder = ObjectConvertUtil.convert(requestVO, IntPaymentOrder.class).get();
            intPaymentOrder.setCurrency(requestVO.getCurrency()==null?CURRENCY:requestVO.getCurrency()); //币种固定为美金
            intPaymentOrder.setUserId(requestVO.getUid());
            intPaymentOrder.setFlowId(flowId);
            intPaymentOrder.setPayStatus(FlowStatusEnum.WAITING.getCode());
            intPaymentOrder.setPaymentChannel(requestVO.getPayChannel());
            intPaymentOrder.setPaymentWay(requestVO.getPayWay());
            intPaymentOrder.setOrderApp(requestVO.getBusinessType());
            intPaymentOrder.setUserTel(requestVO.getUserTel());
            //intPaymentOrder.setDiscount(requestVO.getDiscount());
            //intPaymentOrder.setSlottingRate(this.getSlottingRate(requestVO.getPayChannel(), requestVO.getPayWay()));

            intPaymentOrderMapper.insert(intPaymentOrder);
            intFlowDetailService.save(flowId, intPaymentOrder.getPaymentId(), requestVO);

            // 渠道支付：调用创建paypal支付订单方法
            Result<HttpResponse<Order>> createResult = paypalPayChannel.createOrder(requestVO);
            BusinessErrorInfoEnum.CHANNEL_CREATE_PAY_FAIL.assertIsTrue(createResult.getRet() == 0);

            HttpResponse<Order> response=createResult.getData();
            String json = JsonUtil.toJsonString(response.result());

            log.info("createOrder response body: {}", json);

            String approve = "";
            PaypalCreateResVO createResVO=new PaypalCreateResVO();
            for (LinkDescription link : response.result().links()) {
                log.info("Links-{}: {}    \tCall Type: {}", link.rel(), link.href(), link.method());
                if (link.rel().equals("approve")) {
                    approve = link.href();
                }
            }
            String totalAmount = response.result().purchaseUnits().get(0).amountWithBreakdown().currencyCode()
                    + ":" + response.result().purchaseUnits().get(0).amountWithBreakdown().value();
            log.info("Total Amount: {}", totalAmount);

            createResVO.setApprovalUrl(approve);
            createResVO.setCreatePayInfo(json);
            createResVO.setTradeNo(response.result().id());//paypal支付订单号
            createResVO.setIntent(response.result().checkoutPaymentIntent());
            createResVO.setCreatetime(response.result().createTime());
            createResVO.setState(response.result().status());

            //支付订单创建状态：
            String state = response.result().status();
            //支付订单创建单号
            String id=response.result().id();
            // 更新渠道单号和状态
            handleOrderUpdate(intPaymentOrder, id,state, requestVO);

            // 此方法没有事务，可以直接抛出创建支付失败异常
            BusinessErrorInfoEnum.CHANNEL_CREATE_PAY_FAIL.assertIsTrue(PaypalStateEnum.CREATED.getCode().equals(state));

            createResVO.setFlowId(flowId);
            createResVO.setPaymentId(requestVO.getPaymentId());
            createResVO.setAmount(requestVO.getAmount());

            return createResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CREATE_PAY_FAIL, null, e.getMessage(), e);
        }
    }


    /**
     * 更新渠道单号或支付失败
     *
     * @param intPaymentOrder
     * @param id   支付渠道，paypal返回的支付订单号
     * @param state  支付订单创建状态
     * @param paypalCreateReqVO
     */
    private void handleOrderUpdate(IntPaymentOrder intPaymentOrder,
                                   String id,
                                   String state,
                                   PaypalCreateReqVO paypalCreateReqVO) {
        IntPaymentOrder intFlowUpdate = new IntPaymentOrder();
        QueryWrapper<IntPaymentOrder> updateConditions = new QueryWrapper<>();
        updateConditions.eq("flow_id", intPaymentOrder.getFlowId());
        updateConditions.eq("user_id", intPaymentOrder.getUserId());
        if (PaypalStateEnum.CREATED.getCode().equals(state)) {
            intFlowUpdate.setPayStatus(FlowStatusEnum.WAITING.getCode());
            intFlowUpdate.setTradeNo(id); //paypal支付订单号
        } /*else if (PaypalStateEnum.APPROVED.getCode().equals(state)) {
            intFlowUpdate.setPayStatus(FlowStatusEnum.SUCCESS.getCode());
        } else {
            intFlowUpdate.setPayStatus(FlowStatusEnum.FAIL.getCode());
        }*/
        intPaymentOrderMapper.update(intFlowUpdate, updateConditions);
    }


    /**
     * 确认支付
     *
     * @return
     * @throws
     */
    @Override
    public PaypalExecuteResVO executePayment(PaypalExecuteReqVO reqVO) {

        //根据支付流水flowid获取支付流水信息
        IntPaymentOrder intFlow = getIntFlowInfo(reqVO.getFlowId());
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(intFlow);
        BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(FlowStatusEnum.WAITING.getCode().equals(intFlow.getPayStatus())
        ||FlowStatusEnum.FAIL.getCode().equals(intFlow.getPayStatus()));//判断是否已支付成功

        // 针对支付单号加锁
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, reqVO.getPaymentId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            reqVO.setTradeNo(intFlow.getTradeNo());//paypal支付平台支付订单号
            //调用paypal确认支付接口
            Result<HttpResponse<Order>> exeResult = paypalPayChannel.captureOrder(reqVO.getTradeNo());
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(exeResult.getRet()==0);

            HttpResponse<Order> response=exeResult.getData();
            String json = JsonUtil.toJsonString(response.result());
            log.info("captureOrder response body: {}", json);

            String amount = "";
            String state = "";
            String paypalFlow="";//paypal支付平台返回支付流水号（回调时用到）
            boolean updateStatus=false;
            for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
                for (Capture capture : purchaseUnit.payments().captures()) {
                    paypalFlow=capture.id();
                    log.info("Capture id: {}", capture.id());
                    log.info("status: {}", capture.status());
                    log.info("invoice_id: {}", capture.invoiceId());
                    state = capture.status();
                    if (PaypalStateEnum.COMPLETED.getCode().equals(state)) {
                        //进行数据库操作，修改订单状态为已支付成功，尽快发货（配合回调和CapturesGet查询确定成功）
                        log.info("支付成功,状态为=COMPLETED");
                        updateStatus=true;
                    }
                    if (PaypalStateEnum.PENDING.getCode().equals(state)) {
                        log.info("status_details: {}", capture.captureStatusDetails().reason());
                        String reason = "PENDING";
                        if (capture.captureStatusDetails() != null && capture.captureStatusDetails().reason() != null) {
                            reason = capture.captureStatusDetails().reason();
                        }
                        //进行数据库操作，修改订单状态为已支付成功，但触发了人工审核，请审核通过后再发货（配合回调和CapturesGet查询确定成功）
                        log.info("支付成功,状态为=PENDING : {}", reason);
                        updateStatus=true;
                    }

                    amount = capture.amount().value();
                    System.out.println("实付金额：" + capture.amount().value());

                }
            }
            //返回结果处理
            //取出实付金额
            BigDecimal amountB = new BigDecimal(amount);
            BigDecimal InCents = amountB.multiply(new BigDecimal("100.00"));
            Integer amountInCents = InCents.intValue();

            PaypalExecuteResVO resVO = new PaypalExecuteResVO();
            resVO.setPayInfo(json);
            resVO.setPayAmount(amountInCents);
            resVO.setFlowTime(response.result().createTime());
            resVO.setTradeNo(response.result().id()); //paypal支付订单号
            resVO.setTransactionId(paypalFlow); //paypal支付流水号
            resVO.setPayStatus(state.equals(PaypalStateEnum.COMPLETED.getCode())
                    || state.equals(PaypalStateEnum.PENDING.getCode())
                    ? FlowStatusEnum.SUCCESS.getCode()
                    : FlowStatusEnum.FAIL.getCode());
            //更新支付结果：  只更新支付平台流水号，不更新支付状态，状态ipn回调时再更新
            if(updateStatus){
                IntPaymentOrder updateFlow = new IntPaymentOrder();
                QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
                conditions.eq("flow_id", reqVO.getFlowId());
                /*if (PaypalStateEnum.CREATED.getCode().equals(state)) {
                    updateFlow.setPayStatus(resVO.getPayStatus());
                }*/
                updateFlow.setTransactionId(paypalFlow);//paypal支付平台流水号，回调时用到
                intPaymentOrderMapper.update(updateFlow, conditions);
            }
            return resVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, e.getMessage(), e);
        }
    }

    /**
     * 根据flowid获取支付流水
     *
     * @param flowId
     * @return
     */
    @Override
    public IntPaymentOrder getIntFlowInfo(String flowId) {
        QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", flowId);
        return intPaymentOrderMapper.selectOne(conditions);
    }

    /**
     * 退款
     *
     * @param refundReqVO
     * @return
     */
    @Override
    public PaypalRefundResVO refundOrder(PaypalRefundReqVO refundReqVO)  {

        //根据支付流水flowid获取支付流水信息
        IntPaymentOrder intFlow = getIntFlowInfo(refundReqVO.getPayFlowId());
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(intFlow);
        BusinessErrorInfoEnum.CHANNEL_REFUND_NO_PAY.assertIsTrue(FlowStatusEnum.SUCCESS.getCode().equals(intFlow.getPayStatus()));

        // 针对支付流水号加锁
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_REFUND, refundReqVO.getPayFlowId(), 60, 5)) {
            BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertIsTrue(lock.getLockRes());

            final OrgPaymentChannelDetailBO paymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(intFlow.getOrgPaymentChannelId());

            long totalRefundAmount = intRefundFlowMapper.sumAmountByPayFlowId(refundReqVO.getPayFlowId());
            BusinessErrorInfoEnum.CHANNEL_REFUND_AMOUNT_OUT.assertIsTrue(
                        intFlow.getAmount() >= (totalRefundAmount + refundReqVO.getAmount()));

            //获取退款流水号
            String flowId = PaymentIdHelper.getRefund(intFlow.getUserId(), intFlow.getPaymentChannel(), null,paymentChannelDetailBO.getChannelOrderIdPre());
            IntRefundFlow intRefundFlow = new IntRefundFlow();
            intRefundFlow.setFlowId(flowId);
            intRefundFlow.setPaymentId(refundReqVO.getPaymentId());//退款单号
            intRefundFlow.setPayFlowId(refundReqVO.getPayFlowId());//退款对应的支付流水号
            intRefundFlow.setUserId(intFlow.getUserId());
            intRefundFlow.setUserTel(intFlow.getUserTel());
            // 实际退款金额
            intRefundFlow.setAmount(refundReqVO.getAmount());
            // 申请退款金额
            intRefundFlow.setRefundAmount(refundReqVO.getAmount());
            intRefundFlow.setOrderAmount(refundReqVO.getOrderAmount());
            intRefundFlow.setRefundStatus(FlowStatusEnum.WAITING.getCode());
            intRefundFlow.setOrderName(intFlow.getOrderName());
            intRefundFlow.setOrderApp(intFlow.getOrderApp());
            intRefundFlow.setPaymentChannel(intFlow.getPaymentChannel());
            intRefundFlow.setPaymentWay(intFlow.getPaymentWay());
            intRefundFlow.setCreateTime(LocalDateTime.now());
            intRefundFlow.setPayScene(intFlow.getPayScene());
            intRefundFlow.setSlottingRate(intFlow.getSlottingRate());
            intRefundFlow.setCurrency(refundReqVO.getCurrency());

            intRefundFlowMapper.insert(intRefundFlow);
            AtomicInteger subFlowInc = new AtomicInteger(1); // 子订单流水起始值
            List<IntRefundFlowDetail> refundFlowDetails = refundReqVO.getIntSubOrders().stream().map(d -> {
                IntRefundFlowDetail detail = ObjectConvertUtil.convert(d, IntRefundFlowDetail.class).get();
                detail.setFlowId(flowId);
                detail.setPaymentId(refundReqVO.getPaymentId());
                detail.setRefundStatus(FlowStatusEnum.WAITING.getCode());
                detail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(flowId, subFlowInc.get()));
                subFlowInc.getAndIncrement();
                detail.setCurrency(refundReqVO.getCurrency());
                return detail;
            }).collect(Collectors.toList());
            intRefundFlowDetailService.saveBatch(refundFlowDetails);

            // 渠道退款
            RefundOrderBO refundOrderBO = new RefundOrderBO();
            refundOrderBO.setUid(intFlow.getUserId());
            refundOrderBO.setRefundId(flowId);
            refundOrderBO.setOrderId(intFlow.getFlowId());
            refundOrderBO.setTransactionId(intFlow.getTradeNo());//退款用到的渠道单号是paypal支付创建的单号
            refundOrderBO.setAmount(refundReqVO.getAmount());
            refundOrderBO.setOrderAmount(refundReqVO.getOrderAmount());
            refundOrderBO.setDiscount(refundReqVO.getDiscount());
            refundOrderBO.setSubOrders(new ArrayList<>());

            refundReqVO.getIntSubOrders().forEach(refundSubVO -> {
                RefundOrderBO.SubOrderBO subOrderBO = new RefundOrderBO.SubOrderBO();
                subOrderBO.setOrderAmount(refundSubVO.getOrderAmount());
                subOrderBO.setAmount(refundSubVO.getAmount());
                subOrderBO.setSubOrderId(refundSubVO.getSubOrderId());
                subOrderBO.setDiscount(refundSubVO.getDiscount());
                List<IntRefundFlowDetail> collect = refundFlowDetails.stream().filter(r ->
                        r.getSubOrderId().equals(refundSubVO.getSubOrderId())).collect(Collectors.toList());
                subOrderBO.setSubRefundId(collect.get(0).getSubFlowId());// 部分退款时使用
                refundOrderBO.getSubOrders().add(subOrderBO);
            });

            //判断整单退款还是部分退款
            int refundType=0;
            if (refundOrderBO.getAmount().intValue() != intFlow.getAmount()) {
                refundType=1;
            }

            //调用paypal申请退款
            Result<HttpResponse<Refund>> refundResult=paypalPayChannel.refundOrder(refundOrderBO);

            boolean faileFlag=false;
            IntRefundFlow updateRefundFlow = new IntRefundFlow();
            if (refundResult.getRet() != 0) {
                faileFlag=true;
            }else{
                HttpResponse<Refund> response=refundResult.getData();
                log.info("refundOrder response info: {}", "响应码："+response.statusCode()+"======响应内容:"+
                        response.result());
                boolean needUpdateRefundFlow=false;
                if(PaypalStateEnum.COMPLETED.getCode().equals(response.result().status())) {
                    //进行数据库操作，修改状态为已退款（配合回调和退款查询确定退款成功）
                    log.info("退款成功");
                    // 设置实付部分退款金额
                    needUpdateRefundFlow=true;
                    //退款金额
                    String relRefund=response.result().amount().value();//美元 USD  需要转为美分
                    int relAmount=new BigDecimal(relRefund).
                            multiply(new BigDecimal(100)).intValue();
                    updateRefundFlow.setAmount(relAmount);
                    updateRefundFlow.setRefundType(refundType);
                    //退款成功状态和渠道退款单号先不进行更新，回调通知时再做更新
                    //updateRefundFlow.setRefundStatus(FlowStatusEnum.SUCCESS.getCode());//退款状态成功
                    //updateRefundFlow.setTransactionId(response.result().id());//退款渠道退款订单号
                    //updateRefundFlow.setFlowTime( LocalDateTime.now());//退款时间 response.result().createTime()
                    intRefundFlow.setAmount(relAmount);
                }
                if(needUpdateRefundFlow){
                    QueryWrapper<IntRefundFlow> conditions = new QueryWrapper<>();
                    conditions.eq("user_id", intFlow.getUserId());
                    conditions.eq("flow_id", flowId);
                    intRefundFlowMapper.update(updateRefundFlow, conditions);

                    //退款明细表状态先不更新，等ipn回调处理
//                        intRefundFlowDetailService.updateRefundStatus(flowId,null,
//                                refundType==0?ChannelRefundTypeEnum.ALL:ChannelRefundTypeEnum.SPLIT,
//                                FlowStatusEnum.SUCCESS); //更新退款明细表状态
                }
                for (com.paypal.payments.LinkDescription link : response.result().links()) {
                    log.info("Links-{}: {}    \tCall Type: {}", link.rel(), link.href(), link.method());
                }

            }
            //退款失败
            if(faileFlag){
                //退款失败
                QueryWrapper<IntRefundFlow> conditions = new QueryWrapper<>();
                conditions.eq("user_id", intFlow.getUserId());
                conditions.eq("flow_id", flowId);

                updateRefundFlow = new IntRefundFlow();
                updateRefundFlow.setRefundStatus(FlowStatusEnum.FAIL.getCode());
                updateRefundFlow.setFlowTime(LocalDateTime.now());
                updateRefundFlow.setRefundType(refundType);
                intRefundFlowMapper.update(updateRefundFlow, conditions);
                intRefundFlowDetailService.updateRefundStatus(flowId,null,
                        ChannelRefundTypeEnum.ALL,
                        FlowStatusEnum.FAIL);
                //  抛出异常，返回订单中心退款失败
                BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertFail();
            }

            PaypalRefundResVO resVO=new PaypalRefundResVO();
            resVO.setFlowId(intRefundFlow.getFlowId());//退款流水号
            resVO.setPaymentId(intRefundFlow.getPaymentId());//订单中心退款单号
            resVO.setSlottingRate(intRefundFlow.getSlottingRate());
            resVO.setRefundAmount(intRefundFlow.getAmount());//申请款金额 == 实际退款金额？
            //resVO.setRefundStatus(updateRefundFlow.getRefundStatus());
            //resVO.setFlowTime(updateRefundFlow.getFlowTime());
            return resVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL, null, e.getMessage(), e);
        }
    }


    /**
     * 处理支付回调结果
     * @param payResultBO
     *  flag:   传空或者passive 表示被动查询即回调时处理步骤
     *          传initiative  表示主动发起查询
     * @return
     * @throws BusinessException
     */
    @Override
    public IntPaymentOrder payResult(PayResultBO payResultBO,String flag) throws BusinessException {
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_CALLBACK_PAY, payResultBO.getOrderId(), 0, 5)) {
            // 针对流水号加锁，如果并发重复通知，未获取锁的抛出重复异常
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
            conditions.eq("transaction_id", payResultBO.getTransactionId()); //paypal支付流水号
            IntPaymentOrder intPaymentOrder = intPaymentOrderMapper.selectOne(conditions);
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertNotNull(intPaymentOrder);
            if("initiative".equals(flag)&& FlowStatusEnum.SUCCESS.getCode().equals(intPaymentOrder.getPayStatus())){
                //如果已经是支付成功则不再重复更新
                return intPaymentOrder;
            }
            // 如果间隔长时间后，重复通知，通过数据判断，是否抛出重复异常
            //BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_SUCCESS_DUP.assertIsNull(intPaymentOrder.getTradeNo());
            // 判断渠道返回实付金额是否一致   paypal会存在不一致的情况，如何处理？
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_AMOUNT_DIFF.assertIsTrue(
                    (payResultBO.getAmount()+payResultBO.getCouponAmount()) == intPaymentOrder.getAmount());

            IntPaymentOrder updatePayment = new IntPaymentOrder();
            //updatePayment.setTradeNo(intPaymentOrder.getTradeNo());
            //updatePayment.setTransactionId(payResultBO.getTransactionId());
            updatePayment.setPayStatus("success".equals(payResultBO.getStatus()) ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());
            updatePayment.setFlowTime(payResultBO.getPayTime());
            // 修改子订单实收金额
            updatePayment.setAmount(payResultBO.getAmount());

            // 修改子流水的金额
            if(null!=payResultBO.getCouponAmount() && payResultBO.getCouponAmount()>0){
                log.info("银联渠道支付回调 更新流水优惠信息 orderId:{} 优惠金额：{}", payResultBO.getOrderId(),payResultBO.getCouponAmount());
                intFlowDetailService.updateAmount(payResultBO.getOrderId(),payResultBO.getCouponAmount());
                updatePayment.setDiscount(payResultBO.getCouponAmount());
            }
            //根据paypal支付中心的支付流水号更新
            QueryWrapper<IntPaymentOrder> updateCondition = new QueryWrapper<>();
            updateCondition.eq("flow_id", intPaymentOrder.getFlowId()); //支付中心流水号
            intPaymentOrderMapper.update(updatePayment, updateCondition);

            intPaymentOrder.setAmount(payResultBO.getAmount());
            //intPaymentOrder.setTradeNo(updatePayment.getTradeNo());
            intPaymentOrder.setPayStatus(updatePayment.getPayStatus());
            intPaymentOrder.setFlowTime(updatePayment.getFlowTime());
            intPaymentOrder.setUserId(payResultBO.getUid());//买家id
            return intPaymentOrder;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_ERROR, null, e.getMessage(), e);
        }
    }

    /**
     * 功能描述: 关闭订单流水,只关闭支付中心的流水，不用管支付渠道即paypal平台的流水
     */
    @Override
    public Result closeOrder(CloseOrderVO closeOrderVO) throws BusinessException {
        QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", closeOrderVO.getFlowId());

        IntPaymentOrder intPaymentOrder = intPaymentOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(intPaymentOrder);
        BusinessErrorInfoEnum.CHANNEL_CLOSE_STATUS_ERROR.assertIsTrue(FlowStatusEnum.WAITING.getCode().equals(intPaymentOrder.getPayStatus()));

        IntPaymentOrder updateFlow = new IntPaymentOrder();
        updateFlow.setPayStatus(FlowStatusEnum.FAIL.getCode());
        intPaymentOrderMapper.update(updateFlow, conditions);
        return Result.success();
    }

    /*
    *  退款结果
    * flag: 传空或者passive 表示被动查询即回调时处理步骤
    *       传initiative  表示主动发起查询
    * */
    @Override
    public IntRefundFlow refundResult(RefundResultBO refundResultBO,String flag) throws BusinessException {
        //根据退款通知的渠道支付流水号查询系统支付流水表的流水号
        String payflowid=intPaymentOrderMapper.selectFlowIdByTransactionId(refundResultBO.getTradeNo());
        log.info("paypal退款回调，支付渠道支付流水：{},系统支付流水：{}",refundResultBO.getTradeNo(),payflowid);
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_NO_FLOW.assertNotNull(payflowid);

        //根据系统支付流水号查询退款表的退款记录
        QueryWrapper<IntRefundFlow> conditions = new QueryWrapper<>();
        conditions.eq("pay_flow_id", payflowid);
        conditions.notIn("refund_status", FlowStatusEnum.FAIL.getCode());//退款退款状态不为失败的记录
        IntRefundFlow intRefundFlow = intRefundFlowMapper.selectOne(conditions);
        log.info("paypal退款回调，查询退款流水为：{}", JsonUtil.toJsonString(intRefundFlow));

        BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_NO_FLOW.assertNotNull(intRefundFlow);
        if("initiative".equals(flag)&& FlowStatusEnum.SUCCESS.getCode().equals(intRefundFlow.getRefundStatus())){
            //如果已经是退款成功则不再重复更新
            return intRefundFlow;
        }
        String refundId = intRefundFlow.getPaymentId();// 退款单号
        refundResultBO.setRefundId(refundId);

        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_CALLBACK_REFUND, refundId, 0, 5)) {
            // 针对流水号加锁，如果并发重复通知，未获取锁的抛出重复异常
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_DUP.assertIsTrue(lock.getLockRes());

            BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_SUCCESS_DUP.assertIsTrue
                    (!FlowStatusEnum.SUCCESS.getCode().equals(intRefundFlow.getRefundStatus()));

            IntRefundFlow updateRefundFlow = new IntRefundFlow();
            updateRefundFlow.setTransactionId(refundResultBO.getTransactionId());//支付渠道退款流水号
            updateRefundFlow.setTradeNo(refundResultBO.getTradeNo());//支付渠道支付流水号
            updateRefundFlow.setFlowTime(refundResultBO.getRefundTime());
            updateRefundFlow.setAmount(refundResultBO.getAmount());//实际退款总金额
            updateRefundFlow.setRefundStatus("success".equals(refundResultBO.getStatus())
                    ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());
            QueryWrapper<IntRefundFlow> updateConditions = new QueryWrapper<>();
            updateConditions.eq("id", intRefundFlow.getId());
            intRefundFlowMapper.update(updateRefundFlow, updateConditions);

            // 修改退款明细退款状态
            intFlowDetailService.updateRefundStatus(refundResultBO.getRefundId(),null,ChannelRefundTypeEnum.ALL,
                    "success".equals(refundResultBO.getStatus()) ? FlowStatusEnum.SUCCESS : FlowStatusEnum.FAIL);

            intRefundFlow.setTransactionId(refundResultBO.getTransactionId());
            intRefundFlow.setTradeNo(refundResultBO.getTradeNo());
            intRefundFlow.setRefundStatus(updateRefundFlow.getRefundStatus());
            intRefundFlow.setFlowTime(updateRefundFlow.getFlowTime());
            intRefundFlow.setAmount(refundResultBO.getAmount());//实际退款金额
            return intRefundFlow;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_ERROR, null, e.getMessage(), e);
        }
    }

    /**
     * 调用paypal查询接口查询paypal支付信息
     * @param paymentId
     * @return
     */
    @Override
    public Result<PaypalQueryResVO> getPaypal(String paymentId) {
        //查询paypal创建支付的订单号
        QueryWrapper<IntPaymentOrder> conditions = new QueryWrapper<>();
        conditions.eq("payment_id", paymentId);
        IntPaymentOrder intPaymentOrder = intPaymentOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(intPaymentOrder);
        log.info("paypal查询接口，支付渠道订单号：{},支付中心支付单号：{}",intPaymentOrder.getTradeNo(),paymentId);
        //调用paypal查询接口
        Result<HttpResponse<com.paypal.orders.Order>> queryResult=paypalPayChannel.getPaypalInfo(intPaymentOrder.getTradeNo());
        if(queryResult.getRet()!=0){
            return Result.fail(queryResult.getMsg(),null);
        }
        HttpResponse<com.paypal.orders.Order> queryResponse =queryResult.getData();
        log.info("响应码："+queryResponse.statusCode());
        String  ordersjson = JsonUtil.toJsonString(queryResponse.result());

        log.info("paypal支付订单查询结果为: {}", ordersjson);
        PaypalQueryResVO resVO=new PaypalQueryResVO();
        resVO.setPaypalId(queryResponse.result().id());
        resVO.setPaypalInfo(ordersjson);

        //paypal支付状态
        String status="";
        LocalDateTime paytime= LocalDateTime.now();
        if(queryResponse.result()!=null&&queryResponse.result().purchaseUnits()!=null
                &&queryResponse.result().purchaseUnits().get(0).payments()!=null
                &&queryResponse.result().purchaseUnits().get(0).payments().captures()!=null
                &&queryResponse.result().purchaseUnits().get(0).payments().captures().get(0)!=null
                &&queryResponse.result().purchaseUnits().get(0).payments().captures().get(0).status()!=null){
            status=queryResponse.result().purchaseUnits().get(0).payments().captures().get(0).status();
            //支付时间为captures下面的create_time
            //接口返回时间【UTC时间】2021-10-27T06:23:09Z  , 实际支付时间【北京时间】 2021-10-27 14:23:09   8h时差
            String pay_time=queryResponse.result().purchaseUnits().get(0).payments().captures().get(0).createTime();
            if(pay_time!=null&&!"".equals(pay_time)){
                try {
                    paytime= DateTimeUtil.Utc2Beijing(pay_time);
                } catch (ParseException e) {
                    log.error("支付时间解析异常{}", e.getMessage());
                }
            }
        }
        String re_status=status;
        if(PaypalStateEnum.GET_COMPLETED.getCode().equals(status)
                ||PaypalStateEnum.GET_PENDING.getCode().equals(status)){
            re_status="COMPLETED";

            //paypal回调参数解析处理
            PayResultBO payResultBO = new PayResultBO();
            payResultBO.setStatus(FlowStatusEnum.SUCCESS.getCode());
            payResultBO.setUid(queryResponse.result().payer().payerId()); //用户id
            payResultBO.setOrderId(queryResponse.result().purchaseUnits().get(0).payments().captures().get(0).id());//交易系统的支付流水号: paypal支付流水号
            payResultBO.setTransactionId(payResultBO.getOrderId());
            payResultBO.setTradeNo(resVO.getPaypalId());//收单机构的单号
          /*  payResultBO.setAmount(new BigDecimal(queryResponse.result().purchaseUnits().get(0).payments().captures().get(0)
                    .sellerReceivableBreakdown().grossAmount().value()).
                    multiply(new BigDecimal(100)).intValue());//实际支付金额，结果是美元，转成美分*/
            payResultBO.setAmount(new BigDecimal(queryResponse.result().purchaseUnits().get(0).payments().captures().get(0)
                    .amount().value()).
                    multiply(new BigDecimal(100)).intValue());//实际支付金额，结果是美元，转成美分
            payResultBO.setMetadata("");
            // 根据实际情况修改优惠金额
            payResultBO.setCouponAmount(0);
            payResultBO.setPayTime(paytime); //paypal回调的付款时间有时区差异，修改为北京时间
            // 修改流水
             IntPaymentOrder intFlow = this.payResult(payResultBO,"initiative");//主动查询

            intFlow.setAmount(payResultBO.getAmount());
            intFlow.setPayStatus(payResultBO.getStatus());
            intFlow.setFlowTime(payResultBO.getPayTime());
            intFlow.setUserId(payResultBO.getUid());//买家id

            PayResultMsgBO payResultMsgBO = new PayResultMsgBO();
            payResultMsgBO.setFlowId(intFlow.getFlowId());
            payResultMsgBO.setPaymentId(intFlow.getPaymentId());
            payResultMsgBO.setUserId(intFlow.getUserId());
            payResultMsgBO.setAmount(intFlow.getAmount());
            payResultMsgBO.setPayTime(intFlow.getFlowTime());
            payResultMsgBO.setStatus(payResultBO.getStatus());
            payResultMsgBO.setTransactionId(intFlow.getTransactionId());
            payResultMsgBO.setTradeNo(intFlow.getTradeNo());
            payResultMsgBO.setOrderApp(intFlow.getOrderApp());
            payResultMsgBO.setPaymentChannel(intFlow.getPaymentChannel());
            payResultMsgBO.setPaymentWay(intFlow.getPaymentWay());
            payResultMsgBO.setPayScene(intFlow.getPayScene());
            payResultMsgBO.setCouponAmount(payResultBO.getCouponAmount());
            resVO.setPayInfo(JsonUtil.toJsonString(payResultMsgBO));
        }
        /*//paypal退款状态
        if(PaypalStateEnum.GET_PARTIALLY_REFUNDED.getCode().equals(status)
                ||PaypalStateEnum.GET_REFUNDED.getCode().equals(status)){
            String refund_traId="";
            String amount="";
            LocalDateTime refundTime=LocalDateTime.now();
            if(queryResponse.result().purchaseUnits().get(0).payments().refunds()!=null){
                refund_traId=queryResponse.result().purchaseUnits().get(0).payments().refunds().get(0).id();
                amount=queryResponse.result().purchaseUnits().get(0).payments().refunds().get(0).sellerPayableBreakdown().grossAmount().value();
                //退款时间为refunds下面的create_time
                //接口返回时间 2021-10-27T00:33:23-07:00  , 实际退款时间【北京时间】 2021-10-27 15:33:23
                String refund_time=queryResponse.result().purchaseUnits().get(0).payments().refunds().get(0).createTime();
                if(refund_time!=null&&!"".equals(refund_time)){
                    try {
                        refundTime=DateTimeUtil.GMT2Beijing(refund_time);
                    } catch (ParseException e) {
                        log.error("退款时间解析异常{}", e.getMessage());
                    }
                }
            }
            re_status="REFUNDED";
            //退款结果处理
            RefundResultBO refundResultBO=new RefundResultBO();
            refundResultBO.setStatus(FlowStatusEnum.SUCCESS.getCode());
            refundResultBO.setTransactionId(refund_traId);//paypal 退款流水号
            refundResultBO.setTradeNo(resVO.getPaypalId());//paypal退款交易号对应的支付流水号
            refundResultBO.setAmount(Math.abs(new BigDecimal(amount).
                    multiply(new BigDecimal(100)).intValue()) );//转为正数的美分
            refundResultBO.setRefundAmount(refundResultBO.getAmount());
            refundResultBO.setRefundTime(refundTime); //时间修改为北京时间
            IntRefundFlow intRefundFlow = this.refundResult(refundResultBO,"initiative"); //主动查询
        }*/
        resVO.setStatus(re_status);
        return Result.success(resVO);
    }

}
